/*  -*- mode: c++; coding: utf-8; c-file-style: "stroustrup"; -*-

    Copyright 2008 Asier Aguirre <asier.aguirre@gmail.com>
    This file is part of Intromit.

    Intromit is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Intromit is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Intromit.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef RENDER_OBJ_HH
#define RENDER_OBJ_HH
//! \addtogroup introsight_framework
//@{

// INCLUDES
#include "util.hh"
#include "refcnt.hh"
#include "matrix.hh"
#include "render_mng.hh"
#include "introsight.hh"
#include "ogl.hh"

// DEFINES
#define RO_MODULE(v)      sqrt((v)[IS_X]*(v)[IS_X] + (v)[IS_Y]*(v)[IS_Y] + (v)[IS_Z]*(v)[IS_Z])
#define RO_MODULE_NSQR(v) ((v)[IS_X]*(v)[IS_X] + (v)[IS_Y]*(v)[IS_Y] + (v)[IS_Z]*(v)[IS_Z])
#define RO_SCA_PROD(x, y) ((x)[IS_X]*(y)[IS_X] + (x)[IS_Y]*(y)[IS_Y] + (x)[IS_Z]*(y)[IS_Z])
#define RO_VEC_PROD(x, y) {(x)[IS_Y]*(y)[IS_Z]-(x)[IS_Z]*(y)[IS_Y], \
                           (x)[IS_Z]*(y)[IS_X]-(x)[IS_X]*(y)[IS_Z], \
                           (x)[IS_X]*(y)[IS_Y]-(x)[IS_Y]*(y)[IS_X]}


class render_attr;

class render_obj: public reference_counter
{
protected:
    // modelview transformation
    matrix4x4 modelview;
    bool modelview_required;

    // projection transformation
    matrix4x4 *projection;

    // render obj children
    render_obj **ro;
    int n_ro, max_ro;

    // render attributes
    render_attr *attr;

    // render results for the node
    unsigned required_pass;  // each bit represents a pass; it is calculated in PRECALC pass (bit0 assumed to be 1)
                             // it represents the requied pass of this renderer OR required_pass of child[0] OR ... OR [n-1]
                             // includes RENDER_PASS_SELECT
    render_pass forced_pass; // objects forces a new render starting at given pass; or RENDER_PASS_LAST

    // render object status
    unsigned status;         // IS_STATUS_*

    // PRIVATE METHODS
    void ctor();
    void dtor();

    // init/finish result records
    void render_init(render_mng *ctx);
    void render_finish(render_mng *ctx);
    // render required children and updates result records
    void render_children(render_mng *ctx);
    void render_children(render_mng *ctx, void (*callback)(render_mng *ctx, int i, void *data), void *callback_data);

    // select utils
    static void select_always(float z); // z=[-1:1] where 1 is furthest and -1 is nearest

public:
    render_obj();
    virtual ~render_obj();

    virtual bool init(render_mng *ctx);
    virtual void render(render_mng *ctx);
    virtual bool event(render_mng *ctx);   // returns true if event managed by object
    virtual unsigned get_type();           // returns IS_TYPE_*

    // getters
    inline unsigned get_required_pass() {return required_pass | 1<<RENDER_PASS_PRECALC;}
    inline render_pass get_forced_pass() {return forced_pass;}
    inline void inc_forced_pass() {forced_pass=(render_pass)((int)forced_pass+1);}
    inline int get_n_ro() {return n_ro;}
    inline unsigned get_status() {return status;}
    inline matrix4x4 *get_modelview() {return &modelview;}  // this is object *only* modelview
    inline matrix4x4 *get_projection() {return projection;} // this is object *only* projection

    // object projection matrix
    void set_projection(double m[IS_MAT4X4]);

    // check object visibility
    bool modelview_projection_visibility(double obj_radius, double mv[IS_MAT4X4]=NULL); // uses modelview and projections matrices from OpenGL

    // distance from camera
    double get_modelview_distance_nsqr(); // obtained from OpenGL modelview matrix (not square root-ed)
    inline double get_modelview_distance_nsqr(double mv[IS_MAT4X4]) {return RO_MODULE_NSQR(&mv[12]);}

    // get modelview/projection matrix
    inline void get_modelview_matrix(double mv[IS_MAT4X4]) {glGetDoublev(GL_MODELVIEW_MATRIX, mv);}
    inline void get_projection_matrix(double mv[IS_MAT4X4]) {glGetDoublev(GL_PROJECTION_MATRIX, mv);}

    // hierarchically sets forced pass in the path of obj
    bool set_hierarchical_forced_pass(render_pass forced_pass, render_obj *obj);

    // utilities
    static void angles_from_vectors(double lookat[IS_3D], double vertical[IS_3D], double angles[IS_SPHERIC]);
};

//@}
#endif

/* Offline metadata attributes:

   @metadata_attrs(
	render_obj
	{
		# members
		modelview, modelview_required,
		projection,
		ro, n_ro, max_ro, attr:
						select

		# methods
		init, render:
						select
	}
   )
*/
